﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
#if !WinRT
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Interactivity;
using LogoFX.Core;
using EventTrigger = System.Windows.Interactivity.EventTrigger;


namespace LogoFX.Infra.Interactivity
{
#if WinRT
    [ContentProperty(Name="Value")]
#else
    [ContentProperty("Value")]
#endif
    [Obsolete("Use Microsoft.Expression.Interactivity.Core.ChangePropertyAction if possible")]
    public class SetPropertyAction 
#if WP7
        : BindableTriggerAction<FrameworkElement>
#elif WinRT
        : TriggerAction<FrameworkElement>
#else
        : TriggerAction<DependencyObject>
#endif
    {
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.Register("PropertyName", typeof(string), typeof(SetPropertyAction),
            new PropertyMetadata(null));

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(SetPropertyAction),
            new PropertyMetadata(null));

#region Properties

        // it might be possible to do
        //public Binding TargetBinding { get; set; }

        
        public string PropertyName
        {
            get { return Convert.ToString(GetValue(PropertyNameProperty)); }
            set { SetValue(PropertyNameProperty, value); }
        }

        public string Type { get; set; }

        
#if WP7
        [TypeConverter(typeof(nRoute.Components.TypeConverters.ConvertFromStringConverter))]
#endif
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Object Value
        {
            get { return GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

#if WP7

        
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Binding ValueBinding
        {
            get { return GetBinding(ValueProperty); }
            set { SetBinding<object>(ValueProperty, value); }
        }

#endif

#endregion

#region Trigger Related

        private static IEnumerable<Assembly> AssembliesToSearch
        {
            get
            {
                // Start with the System.Windows assembly (home of all core controls)
                yield return typeof(Control).Assembly;
                
#if SILVERLIGHT
                // Fall back by trying each of the assemblies in the Deployment's Parts list
                foreach (var part in Deployment.Current.Parts)
                {
                    var streamResourceInfo = Application.GetResourceStream(
                        new Uri(part.Source, UriKind.Relative));
                    using (var stream = streamResourceInfo.Stream)
                    {
                        yield return part.Load(stream);
                    }
                }
#else
                yield return Assembly.GetEntryAssembly();
                foreach (Assembly referencedAssembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    yield return referencedAssembly;
                }
#endif
            }
        }

        protected override void Invoke(object parameter)
        {
            if (string.IsNullOrEmpty(this.PropertyName) || this.AssociatedObject == null) return;

            System.Type type = null;
            if (null == Type)
            {
                // No type specified; setting for the specified element
                type = AssociatedObject.GetType();
            }
            else
            {
                // Try to get the type from the type system
                try
                {
                    type = System.Type.GetType(Type);
                }
                catch
                {
                }
                if (null == type)
                {
                    // Search for the type in the list of assemblies
                    foreach (var assembly in AssembliesToSearch)
                    {
                        // Match on short or full name
                        
                        type = assembly.GetTypes()
                            .Where(t => (t.FullName == Type) || (t.Name == Type))
                            .FirstOrDefault();
                        if (null != type)
                        {
                            // Found; done searching
                            break;
                        }
                    }
                    if (null == type)
                    {
                        // Unable to find the requested type anywhere
                        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                            "Unable to access type \"{0}\". Try using an assembly qualified type name.",
                            Type));
                    }
                }
            }

            DependencyProperty property = null;
            var field = type.GetField(PropertyName + "Property",
                BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
            if (null != field)
            {
                property = field.GetValue(null) as DependencyProperty;
            }
            if (null == property)
            {
                // Unable to find the requested property
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                    "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
                    PropertyName, type.Name));
            }

            BindingOperations.SetBinding(AssociatedObject, property,
                                         new Binding("Value") {Source = this, Mode = BindingMode.OneTime});

            //// we set the value
            //var propertyInfo = type.GetProperty(this.PropertyName);
            //if (propertyInfo == null || !propertyInfo.CanWrite) return;

            //// else we convert and set the value
            //var value = Value != null ? ConverterHelper.ConvertToType(Value, propertyInfo.PropertyType) : null;
            //propertyInfo.SetValue(this.AssociatedObject, value, null);
        }

#endregion

    }
}
#endif
